<html>
<head>
<script src="../OLLoader.js"></script>
  <script type="text/javascript">
    var supported = OpenLayers.Renderer.Canvas.prototype.supported();
    
    var map, layer;
    function setUp() {
        map = new OpenLayers.Map("map");
        layer = new OpenLayers.Layer.Vector(null, {
            isBaseLayer: true,
            renderers: ["Canvas"]
        });
        map.addLayer(layer);
        map.setCenter(new OpenLayers.LonLat(0, 0));
    }
    
    function tearDown() {
        map.destroy();
        map = null;
        layer = null;
    }
    
    function test_Renderer_Canvas_constructor(t) {
        if (!supported) { t.plan(0); return; }
        t.plan(2);
        var el = document.body;
        el.id = "foo";
        var r = new OpenLayers.Renderer.Canvas(el.id);
        
        t.ok(r instanceof OpenLayers.Renderer.Canvas, "new OpenLayers.Renderer.Canvas returns Renderer.Canvas object" );
        t.ok(r.container == el, "renderer container is correctly set");
        r.destroy();
    }
    
    function test_Renderer_Canvas_setextent(t) {
        if (!supported) { t.plan(0); return; }
        t.plan(2);
        
        setUp();
        
        var r = layer.renderer;        
        var extent = new OpenLayers.Bounds(1,2,3,4);
        r.resolution = 1;
        r.setExtent(extent, true);
        t.ok(r.extent.equals(extent), "extent is correctly set");
        t.eq(r.resolution, null, "resolution nullified");
        
        tearDown();
    }
    
    function test_Renderer_Canvas_setsize(t) {
        if (!supported) { t.plan(0); return; }
        t.plan(2);
        
        var el = document.body;
        el.id = "foo";
        var r = new OpenLayers.Renderer.Canvas(el.id);
        var size = new OpenLayers.Size(1,2);
        r.resolution = 1;
        r.setSize(size);
        t.ok(r.size.equals(size), "size is correctly set");
        t.eq(r.resolution, null, "resolution nullified");
        r.destroy();
    }
    
    function test_Renderer_Canvas_getresolution(t) {
        if (!supported) { t.plan(0); return; }
        t.plan(2);
        
        var el = document.body;
        el.id = "foo";
        var r = new OpenLayers.Renderer.Canvas(el.id);
        var map = new OpenLayers.Map("map");
        r.map = map;
        var resolution = r.getResolution();
        t.eq(resolution, map.getResolution(), "resolution matches the map resolution");
        t.eq(r.resolution, resolution, "resolution is correctly set");
        map.destroy();
    }

    function test_featureIdToHex(t) {
        if (!supported) {
            t.plan(0); 
            return;
        }
        t.plan(2);
        var el = document.body;
        el.id = "foo";
        var renderer = new OpenLayers.Renderer.Canvas(el.id);
        
        var cases = [{
            id: "foo_0", hex: "#000001"
        }, {
            id: "foo_10", hex: "#00000b"
        }, {
            id: "foo_100", hex: "#000065"
        }, {
            id: "foo_1000000", hex: "#0f4241"
        }, {
            id: "foo_16777214", hex: "#ffffff"
        }, {
            id: "foo_16777215", hex: "#000001"
        }];
        t.plan(cases.length);
        
        var c;
        for (var i=0; i<cases.length; ++i) {
            c = cases[i];
            t.eq(renderer.featureIdToHex(c.id), c.hex, c.id);
        }
        
        renderer.destroy();
    }


    function test_Renderer_Canvas_destroy(t) {
        if (!supported) { t.plan(0); return; }
        t.plan(5);

        var el = document.body;
        el.id = "foo";
        var r = new OpenLayers.Renderer.Canvas(el.id);
        r.container = document.createElement("div");
        r.extent = new OpenLayers.Bounds(1,2,3,4);
        r.size = new OpenLayers.Size(1,2);
        r.resolution = 1;
        r.map = {};

        r.destroy();

        t.eq(r.container, null, "container nullified");
        t.eq(r.extent, null, "extent nullified");
        t.eq(r.size, null, "size nullified");
        t.eq(r.resolution, null, "resolution nullified");
        t.eq(r.map, null, "map nullified");
    }

    function test_drawFeature(t) {
        if (!supported) {
            t.plan(0); 
            return; 
        }
        
        t.plan(10);
        
        setUp();
        
        var renderer = layer.renderer;
        var count = 0;
        var redraw = layer.renderer.redraw;
        renderer.redraw = function() {
            ++count;
            redraw.apply(this, arguments);
        }
        var exp;
        
        // a) draw a point feature
        count = 0;
        exp = renderer.drawFeature(
            new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 0)), {}
        );
        t.eq(exp, true, "a) drawFeature returns true");
        t.eq(count, 1, "a) redraw called once after drawing a point feature");
        renderer.clear();
        
        // b) draw one feature with no geometry
        count = 0;
        exp = renderer.drawFeature(
            new OpenLayers.Feature.Vector(), {}
        );
        t.eq(exp, undefined, "b) drawFeature returns undefined");
        t.eq(count, 0, "b) redraw is not called when drawing a feature with no geometry");
        renderer.clear();
        
        // c) draw a point feature with display "none"
        count = 0;
        exp = renderer.drawFeature(
            new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1, 0)),
            {display: "none"}
        );
        t.eq(exp, false, "c) drawFeature returns false");
        t.eq(count, 1, "c) redraw is called when drawing a feature with display 'none'");
        renderer.clear();

        // d) draw a point feature outside renderer extent
        count = 0;
        exp = renderer.drawFeature(
            new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(-1000, 0)), {}
        );
        t.eq(exp, false, "d) drawFeature returns false");
        t.eq(count, 1, "d) redraw is called when drawing a feature outside renderer extent");
        renderer.clear();
        
        // e) draw a polygon feature without bounds
        count = 0;
        exp = renderer.drawFeature(
            new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Polygon()), {}
        );
        t.eq(exp, false, "d) drawFeature returns false");
        t.eq(count, 1, "d) redraw is called when drawing a feature without bounds");
        renderer.clear();

        tearDown();
    }


    function test_pendingRedraw(t) {
        if (!supported) {
            t.plan(0); 
            return; 
        }
        
        t.plan(4);
        var layer = new OpenLayers.Layer.Vector(null, {
            isBaseLayer: true,
            renderers: ["Canvas"]
        });
        
        var map = new OpenLayers.Map({
            div: "map",
            controls: [],
            layers: [layer],
            center: new OpenLayers.LonLat(0, 0),
            zoom: 0
        });
        
        var count = 0;
        var redraw = layer.renderer.redraw;
        layer.renderer.redraw = function() {
            ++count;
            redraw.apply(this, arguments);
        }
        
        // add one point feature and confirm redraw is called once
        count = 0;
        layer.addFeatures([
            new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 0))
        ]);
        t.eq(count, 1, "redraw called once after adding one point feature");
        
        // add one feature with no geometry and confirm redraw is not called
        count = 0;
        layer.addFeatures([
            new OpenLayers.Feature.Vector()
        ]);
        t.eq(count, 0, "redraw is not called when adding a feature with no geometry");
        
        // add one point feature, one feature with no geom, and one point feature and confirm redraw is called once
        count = 0;
        layer.addFeatures([
            new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1, 0)),
            new OpenLayers.Feature.Vector(),
            new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 1))
        ]);
        t.eq(count, 1, "redraw called once after adding three features where middle one has no geometry");

        // add two point features and one feature with no geom, and confirm redraw is called once
        count = 0;
        layer.addFeatures([
            new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(1, 0)),
            new OpenLayers.Feature.Vector(new OpenLayers.Geometry.Point(0, 1)),
            new OpenLayers.Feature.Vector()
        ]);
        t.eq(count, 1, "redraw called once after adding three features where last one has no geometry");
        
        map.destroy();
    }
    
    function test_hitDetection(t) {
        if (!supported) {
            t.plan(0); 
            return; 
        }
        
        var layer = new OpenLayers.Layer.Vector(null, {
            isBaseLayer: true,
            resolutions: [1],
            styleMap: new OpenLayers.StyleMap({
                pointRadius: 5,
                strokeWidth: 3,
                fillColor: "red",
                fillOpacity: 0.5,
                strokeColor: "blue",
                strokeOpacity: 0.75
            }),
            renderers: ["Canvas"]
        });
        
        var map = new OpenLayers.Map({
            div: "map",
            controls: [],
            layers: [layer],
            center: new OpenLayers.LonLat(0, 0),
            zoom: 0
        });
        
        layer.addFeatures([
            new OpenLayers.Feature.Vector(
                new OpenLayers.Geometry.Point(-100, 0)
            ),
            new OpenLayers.Feature.Vector(
                OpenLayers.Geometry.fromWKT("LINESTRING(-50 0, 50 0)")
            ),
            new OpenLayers.Feature.Vector(
                OpenLayers.Geometry.fromWKT("POLYGON((100 -25, 150 -25, 150 25, 100 25, 100 -25), (120 -5, 130 -5, 130 5, 120 5, 120 -5))")
            ),
            new OpenLayers.Feature.Vector(
                new OpenLayers.Geometry.Point(80, 0), {}, {
                    graphicName: "square",
                    pointRadius: 8,
                    strokeWidth: 4,
                    fillColor: "red",
                    fillOpacity: 0.5,
                    strokeColor: "blue",
                    strokeOpacity: 0.75
                } 
            )
        ]);
        
        var cases = [{
            msg: "center of point", x: -100, y: 0, id: layer.features[0].id
        }, {
            msg: "edge of point", x: -103, y: 3, id: layer.features[0].id
        }, {
            msg: "outside point", x: -110, y: 3, id: null
        }, {
            msg: "center of line", x: 0, y: 0, id: layer.features[1].id
        }, {
            msg: "edge of line", x: 0, y: 1, id: layer.features[1].id
        }, {
            msg: "outside line", x: 0, y: 5, id: null
        }, {
            msg: "inside polygon", x: 110, y: 0, id: layer.features[2].id
        }, {
            msg: "edge of polygon", x: 99, y: 0, id: layer.features[2].id
        }, {
            msg: "inside polygon hole", x: 125, y: 0, id: null
        }, {
            msg: "outside polygon", x: 155, y: 0, id: null
        }, {
            msg: "inside symbol", x: 80, y: 0, id: layer.features[3].id
        }, {
            msg: "outside symbol interior, inside symbol edge", x: 90, y: 8, id: layer.features[3].id
        }, {
            msg: "outside symbol", x: 94, y: 0, id: null
        }];

        function px(x, y) {
            return map.getPixelFromLonLat(
                new OpenLayers.LonLat(x, y)
            );
        }        
        
        var num = cases.length;
        t.plan(2 * num);
        var c, feature;
        for (var i=0; i<num; ++i) {
            c = cases[i];
            feature = layer.renderer.getFeatureIdFromEvent({xy: px(c.x, c.y)});
            t.eq(feature && feature.id, c.id, c.msg);
            
            // Extra test: hit detection on an invisible canvas should return undefined
            layer.setVisibility(false);
            feature = layer.renderer.getFeatureIdFromEvent({xy: px(c.x, c.y)});
            t.eq(feature, undefined, c.msg + ' (invisible)');
            layer.setVisibility(true);
        }
        
        map.destroy();
        
    }

    // see http://trac.osgeo.org/openlayers/ticket/3264
    function test_externalGraphic_destroyFeatures(t) {
        if (!supported) {
            t.plan(0);
            return;
        }

        t.plan(1);

        // set up

        var layer = new OpenLayers.Layer.Vector(null, {
            isBaseLayer: true,
            renderers: ["Canvas"]
        });

        var map = new OpenLayers.Map({
            div: "map",
            controls: [],
            layers: [layer],
            center: new OpenLayers.LonLat(0, 0),
            zoom: 0
        });

        layer.addFeatures([
            new OpenLayers.Feature.Vector(
                new OpenLayers.Geometry.Point(0, 0),
                null,
                {
                    externalGraphic: '../../img/marker.png',
                    graphicHeight: 20,
                    graphicWidth: 20
                }
            )
        ]);

        var called = false;
        layer.renderer.canvas.drawImage = function(img, x, y, w, h) {
            called = true;
        };

        // test

        // schedule a canvas.drawImage
        layer.renderer.redraw();

        // destroy the feature before drawImage gets called
        layer.destroyFeatures();

        t.delay_call(0.1, function() {
            t.ok(!called,
                 'canvas.drawImage not called if feature is destroyed');

            // tear down
            map.destroy();
        });
    }

    // see http://trac.osgeo.org/openlayers/ticket/3264
    function test_externalGraphic_moveTo(t) {
        if (!supported) {
            t.plan(0);
            return;
        }

        t.plan(2);

        // set up

        var layer = new OpenLayers.Layer.Vector(null, {
            isBaseLayer: true,
            renderers: ["Canvas"]
        });

        var map = new OpenLayers.Map({
            div: "map",
            controls: [],
            layers: [layer],
            center: new OpenLayers.LonLat(0, 0),
            zoom: 0
        });

        var feature = new OpenLayers.Feature.Vector(
            new OpenLayers.Geometry.Point(0, 0),
            null,
            {
                externalGraphic: '../../img/marker.png',
                graphicHeight: 20,
                graphicWidth: 20,
                graphicXOffset: 0,
                graphicYOffset: 0
            }
        );

        layer.addFeatures([feature]);

        // test

        // delay_call to let the first drawImage (the one
        // resulting from addFeatures) run
        t.delay_call(0.1, function() {

            var log = [];
            layer.renderer.canvas.drawImage = function(img, x, y, w, h) {
                log.push({x: x, y: y});
            };

            layer.renderer.redraw();
            map.setCenter(new OpenLayers.LonLat(45, 0), 0);

            t.delay_call(0.1, function() {
                t.eq(log.length, 2,
                     "canvas.drawImage called twice");
                t.ok(log[0].x == log[1].x && log[0].y == log[1].y,
                     "image drawn at the same location");

                // tear down
                map.destroy();
            });
        });
    }

  </script>
</head>
<body>
<div id="map" style="width:500px;height:550px"></div>
</body>
</html>
